#ifndef MIPS_INSTR_H
#define MIPS_INSTR_H
#include <assert.h>
#define MIPS32_BITS(value, start, count) (((value) >> (start)) & ((1 << (count)) - 1))
#define MIPS32_SIGN(value, signbit) (-(((value) >> (signbit)) & 1))

#define MIPS32_ENCODE_I_IMM(imm) (MIPS32_BITS((imm), 0, 16) << 0)
#define MIPS32_ENCODE_R_IMM(imm) (MIPS32_BITS((imm), 0, 4) << 6)
#define MIPS32_ENCODE_J_IMM(imm) (MIPS32_BITS((imm), 0, 26) << 0)

#define MIPS32_DECODE_I_IMM(ins) ((MIPS32_BITS((ins), 0, 16) << 0) | (MIPS32_SIGN(ins,15) << 16))
#define MIPS32_DECODE_U_IMM(ins) ((MIPS32_BITS((ins), 0, 16) << 0))
#define MIPS32_DECODE_R_IMM(ins) (MIPS32_BITS((ins), 0, 4) << 6)
#define MIPS32_DECODE_J_IMM(ins) (MIPS32_BITS((ins), 0, 26) << 0)

#define MIPS32_VALID_REG(value) (MIPS32_BITS((value), 0, 5) == (value))

#define MIPS32_VALID_I_IMM(value)                                       \
	(MIPS32_DECODE_I_IMM(MIPS32_ENCODE_I_IMM (value)) == (value))

#define _mips32_emit(p, insn)                   \
	do {                                        \
		*(uint32_t *) (p) = (insn);             \
		(p) += sizeof (uint32_t);               \
	} while (0)


/*
 * The R-type encoding is used for a variety of instructions that operate on
 * registers only, such as most integer instructions, atomic instructions, and
 * some floating point instructions.
 *
 * [31....26] special
 * [25...21] rs
 * [20...16] rt
 * [15...11] rd
 * [10...6]  fmt
 * [5...0]   opcode
 */

#define mips32_r_op(p, opcode, fmt, special, rs, rt, rd)    \
	do {                                                    \
		assert (MIPS32_VALID_REG ((rd)));                   \
		assert (MIPS32_VALID_REG ((rs)));                   \
		assert (MIPS32_VALID_REG ((rt)));                   \
		_mips32_emit ((p), ((opcode) << 0) |                \
                      ((fmt) << 6) |                        \
                      ((rd) << 11) |                        \
                      ((rt) << 16) |                        \
                      ((rs) << 21) |                        \
                      ((special) << 26));                   \
	} while (0)


/*
 * The R4-type encoding is used for a variety of instructions that operate on
 * registers only, such as most integer instructions, atomic instructions, and
 * some R type with imm point instructions.
 *
 * [31....26] special
 * [25...21] rs
 * [20...16] rt
 * [15...11] rd
 * [10...6]  fmt
 * [5...0]   opcode
 */

#define mips32_r4_op(p, opcode, imm,  func, special, rt, rd) \
	do {                                                    \
		assert (MIPS32_VALID_REG ((rd)));                   \
		assert (MIPS32_VALID_REG ((rt)));                   \
		_mips32_emit ((p), ((opcode) << 0) |                \
                      ((imm) << 6) |                        \
                      ((rd) << 11) |                        \
                      ((rt) << 16) |                        \
                      ((func) << 21) |                      \
                      ((special) << 26));                   \
	} while (0)

/*
 * The f-type encoding is used for floating point fused multiply-add instructions.
 *
 * [31....26] special
 * [25...21]  fmt
 * [20...16]  ft
 * [15...11]  fs
 * [10...6]   fd
 * [5...0]   opcode
 */

#define mips32_f_op(p, opcode, fmt, special, fs, ft, fd)    \
	do {                                                    \
		assert (MIPS32_VALID_REG ((fd)));                   \
		assert (MIPS32_VALID_REG ((fs)));                   \
		assert (MIPS32_VALID_REG ((ft)));                   \
		_mips32_emit ((p), ((opcode) << 0) |                \
                      ((fd) << 6) |                         \
                      ((fs) << 11) |                        \
                      ((ft) << 16) |                        \
                      ((fmt) << 21) |                       \
                      ((special) << 26));                   \
	} while (0)


/*
 * The I-type encoding is used for a variety of instructions, such as ADDI,
 * loads, and most integer instructions that operate on immediates.
 *
 * [31...26] opcode
 * [25...21] rs
 * [20...16] rt
 * [16....0] imm[0..16]
 */

#define mips32_i_op(p, opcode, rs, rt, imm)                     \
	do {                                                        \
		assert (MIPS32_VALID_REG ((rs)));                       \
		assert (MIPS32_VALID_REG ((rt)));                       \
        assert(MIPS32_VALID_I_IMM(imm));                        \
		_mips32_emit ((p), ((opcode) << 26) |                   \
                      ((rs) << 21) |                            \
                      ((rt) << 16) |                            \
                      (MIPS32_ENCODE_I_IMM ((int32_t) (imm)))); \
	} while (0)



/*
 * The J-type encoding is used for conditional branches with signed offsets.
 *
 * [31...26] opcode
 * [25....0] imm[26]
 */
#define mips32_j_op(p, opcode, imm)                             \
	do {                                                        \
		_mips32_emit ((p), ((opcode) << 26) |                   \
                      (MIPS32_ENCODE_J_IMM ((int32_t) (imm)))); \
	} while (0)

struct mips_instrs {
    char **code_r_type;
    int r_code_size;
    char **code_r4_type;
    int r4_code_size;
    char **code_i_type;
    int i_code_size;
    char **code_j_type;
    int j_code_size;
    char **code_f_type;
    int f_code_size;
};

extern struct mips_instrs mips_instrs;
#endif
